
/**		
 * 
 *	components.Component
 *	
 *	@version 1.00 | May 28, 2010
 *	@author Fraser Hobbs
 *  
 **/
 
package mx.pacu.components 
{
	import mx.pacu.style.CSS;
	import mx.pacu.style.Styles;

	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.getQualifiedClassName;

	/**
	 * @author Fraser Hobbs
	 */
	public class Component extends Sprite 
	{
		protected var _cssId:String;
		protected var _cssClass:String;
		protected var _hierachy:Array;
		protected var _childComponents:Array = new Array();
		protected var _top:Boolean = true;
		protected var _styles:Styles = new Styles();
		
		protected var _absoluteWidth:Number;
		protected var _absoluteHeight:Number;
		
		protected var _background:Sprite = new Sprite();
		protected var _content:Sprite = new Sprite();
		
		private var _dirty:Boolean = false;
		
		public function Component()
		{
			CSS.addEventListener(Event.CHANGE, cssUpdated);
			
			super.addChild(_background);
			super.addChild(_content);
			
			addEventListener(Event.ADDED, addedHandler);
			addEventListener(Event.REMOVED, removedHandler);
			addEventListener(Event.RENDER, renderHandler);
			
			defaultStyles();
		}
		
		//	----------------------------------------------------------------
		//	PUBLIC METHODS
		//	----------------------------------------------------------------
		
		override public function addChild(child : DisplayObject) : DisplayObject
		{
			return _content.addChild(child);
		}

		override public function removeChild(child : DisplayObject) : DisplayObject
		{
			return _content.removeChild(child);
		}

		//use hierachy passed down add to it and pass on to children
		public final function updateHierachy(...hierachy):void
		{
			hierachy.push([getQualifiedClassName(this),_cssClass,_cssId]);
			
			_hierachy = hierachy;
			
			for (var i : int = 0; i < _childComponents.length; i++)
			{
				_childComponents[i].updateHierachy(hierachy);
			}
			
			updateStyles();
		}
		
		//update view
		public final function update():void
		{
			if(_childComponents.length == 0) invalidate();
		}
		
		//draw view
		public function draw():void
		{
			drawWidth();
			drawHeight();
			drawPadding();
			drawBackground();
			drawMargin();
			
			trace("draw : " + this + " : " + _hierachy);
			
		}
		
		//set default styles
		public function defaultStyles():void
		{
			
		}
		
		//	----------------------------------------------------------------
		//	PROTECTED METHODS
		//	----------------------------------------------------------------
		
		protected function drawWidth():void
		{
			if(_styles.width)
			{
				if(_content.width > _styles.width)
				{
					_absoluteWidth = _content.width;
				}
				else
				{
					_absoluteWidth = _styles.width;
				}
			}
			else
			{
				_absoluteWidth = _content.width;
			}
		}
		
		protected function drawHeight():void
		{
			if(_styles.height)
			{
				if(contentHeight > _styles.height)
				{
					_absoluteHeight = contentHeight;
				}
				else
				{
					_absoluteHeight = _styles.height;
				}
			}
			else
			{
				_absoluteHeight = contentHeight;
			}
		}
		
		protected function drawPadding():void
		{
			if(_styles.paddingLeft)
			{
				_content.x = _styles.paddingLeft;
				_absoluteWidth += _styles.paddingLeft;
			}
			else
			{
				_content.x = 0;
			}
			
			if(_styles.paddingRight)
			{
				_absoluteWidth += _styles.paddingRight;
			}
			
			
			if(_styles.paddingTop)
			{
				_content.y = _styles.paddingTop;
				_absoluteHeight += _styles.paddingTop;
			}
			else
			{
				_content.y = 0;
			}
			
			if(_styles.paddingBottom)
			{
				_absoluteHeight += _styles.paddingBottom;
			}
		}
		
		protected function drawBackground():void
		{
			if(_styles.backgroundColor)
			{
				_background.graphics.beginFill(_styles.backgroundColor);
				_background.graphics.drawRect(0, 0, _absoluteWidth, _absoluteHeight);
				_background.graphics.endFill();
			}
			else
			{
				_background.graphics.clear();
			}
		}
		
		protected function drawMargin():void
		{
			if(_styles.marginLeft)
			{
				_content.x = _styles.marginLeft;
				_absoluteWidth += _styles.marginLeft;
			}
			
			if(_styles.marginRight)
			{
				_absoluteWidth += _styles.marginRight;
			}
			
			if(_styles.marginTop)
			{
				_content.y = _styles.marginTop;
				_absoluteHeight += _styles.marginTop;
			}
			
			if(_styles.marginBottom)
			{
				_absoluteHeight += _styles.marginBottom;
			}
		}
		
		//	----------------------------------------------------------------
		//	GETTER/SETTERS
		//	----------------------------------------------------------------
		
		//set top is value
		public function set top(__top:Boolean):void
		{
			_top = __top;
		}
		
		public function get absoluteWidth():Number
		{
			return _absoluteWidth;
		}
		
		public function get absoluteHeight():Number
		{
			return _absoluteHeight;
		}
		
		public function get contentHeight() : Number
		{
			var h:Number = 0;
			
			for (var i : int = 0; i < _childComponents.length; i++)
			{
				var childHeight:Number = _childComponents[i].x + _childComponents[i].absoluteHeight;
				
				if(childHeight > h) h = childHeight;
			}
			
			return h;
		}
		
		public function get contentWidth() : Number
		{
			var w:Number = 0;
			
			for (var i : int = 0; i < _childComponents.length; i++)
			{
				var childWidth:Number = _childComponents[i].y + _childComponents[i].absoluteHeight;
				
				if(childWidth > w) w = childWidth;
			}
			
			return w;
		}
		
		//get the css class
		public function get cssClass() : String
		{
			return _cssClass;
		}
		
		//set the css class
		public function set cssClass(__cssClass : String) : void
		{
			_cssClass = __cssClass;
			
			if(_top)
			{
				updateHierachy();
			}
		}
		
		//get the css id
		public function get cssId() : String
		{
			return _cssId;
		}
		
		//set the css id
		public function set cssId(__cssId : String) : void
		{
			_cssId = __cssId;
			
			if(_top)
			{
				updateHierachy();
			}
		}
		
		//get components current styles
		public function get styles():Styles
		{
			return _styles;
		}
		
		//get the components current hierachy
		public function get hierachy():Array
		{
			return _hierachy;
		}

		//	----------------------------------------------------------------
		//	PRIVATE METHODS
		//	----------------------------------------------------------------
		
		//update styles based on hierachy
		private function updateStyles(__styles:Styles = null):void
		{
			trace("updateStyles " + this + " : " + __styles);
			
			defaultStyles();
			
			if(__styles)
			{
				for (var property : String in __styles)
				{
					if(Styles.cascades(property) && __styles[property] && !_styles[property])
					{
						_styles[property] = __styles[property];
					}
				}
			}
			
			_styles = CSS.getStyles(_styles, _hierachy);
			
			for (var i : int = 0; i < _childComponents.length; i++)
			{
				_childComponents[i].updateStyles(_styles);
			}
			
			update();
		}
		
		//redraw on render
		private function invalidate():void
		{
			if(!stage) return;
			
			_dirty = true;
			stage.invalidate();
		}
		
		//update children
		private function updateChildren():void
		{
			_childComponents = [];
			
			for (var i : int = 0; i < numChildren; i++)
			{
				_childComponents = _childComponents.concat(findChildComponents(getChildAt(i)));
			}
			
			if(_top)
			{
				updateHierachy();
			}
		}
		
		//find child components in displayobject
		private function findChildComponents(__child : DisplayObject):Array
		{
			var childComponents:Array = [];
			
			if(__child is Component)
			{
				childComponents.push(__child);
				Component(__child).top = false;
				__child.addEventListener(Event.CHANGE, changeHandler);
			}
			else if(__child is DisplayObjectContainer)
			{
				var c:DisplayObjectContainer = __child as DisplayObjectContainer;
				
				for (var i : int = 0; i < c.numChildren; i++)
				{
					childComponents = childComponents.concat(findChildComponents(c.getChildAt(i)));
				}
			}
			
			return childComponents;
		}
		
		//	----------------------------------------------------------------
		//	EVENT HANDLERS
		//	----------------------------------------------------------------
		
		//render event
		private function renderHandler(event : Event) : void
		{
			if(!_dirty) return;
			_dirty = false;
			
			draw();
			
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		//when child has changed invalidate this
		private function changeHandler(event : Event) : void
		{
			invalidate();
		}
		
		private function addedHandler(event : Event) : void
		{
			updateChildren();
		}
		
		private function removedHandler(event : Event) : void
		{
			if(event.target == this) _top = true;
			
			updateChildren();
		}
		
		private function cssUpdated(event : Event) : void
		{
			if(_top) updateStyles();
		}
	}
}